package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.HotArticleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.ArticleVisitStreamMess;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ApArticleServiceImpl  extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    private final Short MAX_PAGE_SIZE = 50;

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private ApArticleContentMapper contentMapper;

    @Autowired
    private ApArticleConfigMapper configMapper;

    @Autowired
    private CacheService cacheService;

    @Override
    public ResponseResult load(ArticleHomeDto dto, Short type) {
        //1. 对分页参数进行校验和修正
        if(null == dto.getSize() || dto.getSize() <= 0){
            dto.setSize(10);
        }

        dto.setSize(Math.min(dto.getSize(), MAX_PAGE_SIZE));
        //2. 对tag(频道数据)进行校验和修正
        if(StringUtils.isEmpty(dto.getTag())){
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }

        //3. 对日期数据进行校验和修正
        if(null == dto.getMinBehotTime()) {
            dto.setMinBehotTime(new Date());
        }
        if(null == dto.getMaxBehotTime()) {
            dto.setMaxBehotTime(new Date());
        }

        //4. 调用mapper查询数据
        List<ApArticle> list = apArticleMapper.load(dto, type);

        //5. 封装并返回
        return ResponseResult.okResult(list);
    }

    /**
     * 加载文章列表
     *
     * @param dto
     * @param type      1 加载更多   2 加载最新
     * @param firstPage true 是首页  false 不是首页
     * @return
     */
    @Override
    public ResponseResult load2(ArticleHomeDto dto, Short type, boolean firstPage) {
        if(firstPage){
            //查询redis
            String json = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if(!StringUtils.isEmpty(json)){
                List<HotArticleVo> voList = JSON.parseArray(json, HotArticleVo.class);
                return ResponseResult.okResult(voList);
            }
        }

        return load(dto, type); //查询数据库
    }

    /**
     * 保存自媒体同步过来的数据
     * 新增
     * 更新
     *
     * @param dto
     * @return
     */
    @Override
    @Transactional
    public ResponseResult saveArticle(ArticleDto dto) {

        if(null == dto || org.apache.commons.lang.StringUtils.isEmpty(dto.getContent())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(dto, apArticle);

        if(null == apArticle.getId()){
            //新增
            apArticleMapper.insert(apArticle);

            ApArticleConfig config = new ApArticleConfig();
            config.setArticleId(apArticle.getId());
            config.setIsComment(Boolean.TRUE);
            config.setIsDown(Boolean.FALSE);
            config.setIsDelete(Boolean.FALSE);
            config.setIsForward(Boolean.TRUE);
            configMapper.insert(config);

            ApArticleContent articleContent = new ApArticleContent();
            articleContent.setContent(dto.getContent());
            articleContent.setArticleId(apArticle.getId());
            contentMapper.insert(articleContent);


        }else{
            //修改
            apArticleMapper.updateById(apArticle);

            ApArticleContent content = contentMapper.selectOne(Wrappers
                    .<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId, apArticle.getId()));
            content.setContent(dto.getContent());
            contentMapper.updateById(content);
        }

        return ResponseResult.okResult(apArticle.getId());
    }

    /**
     * 时时计算更新文章的数据库的数据和redis的数据
     *
     * @param mess
     */
    @Override
    public void upateScore(ArticleVisitStreamMess mess) {
        //1. 更新数据库中的数据
        ApArticle apArticle = updateDB(mess);

        //2. 重新计算文章的得分 同时当前的得分需要*3
        Integer score = computeScore(apArticle);
        score *= 3;

        //3. 更新当前频道的redis中的数据
        replaceCacheByTag(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
       
        //4. 更新推荐中reids的数据
        replaceCacheByTag(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
    }

    /**
     * 通过key更新reids中缓存的数据
     * @param apArticle
     * @param score
     * @param key
     */
    private void replaceCacheByTag(ApArticle apArticle, Integer score, String key) {
        //1. 根据key获取redis中的缓存数据
        String json = cacheService.get(key);
        //2. 判断缓存数据是否存在
        if(org.apache.commons.lang.StringUtils.isNotEmpty(json)){
            //3. 把redis中获取到的json转换为对象-集合对象
            List<HotArticleVo> voList = JSON.parseArray(json, HotArticleVo.class);
            //4. 判断集合的大小是否小于30 如果小于则直接添加
            HotArticleVo vo = new HotArticleVo();
            BeanUtils.copyProperties(apArticle, vo);
            vo.setScore(score);

            Boolean flag = Boolean.FALSE;

            //4.1 判断当前的文章是否已经在volist中
            for (HotArticleVo hotArticleVo : voList) {
                if(hotArticleVo.getId().equals(apArticle.getId())){
                    hotArticleVo.setScore(score);

                    flag = Boolean.TRUE;
                }
            }

            if(!flag){
                if(voList.size() < 30){
                    voList.add(vo);
                }else{
                    //5. 如果大于 则对集合进行重新排序
                    voList = voList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
                    //6. 获取到得分最低的文章 然后跟当前文章进行比较 如果当前文章的分数更大 则删除集合中最后一个元素并添加当前的文章
                    HotArticleVo lastVo = voList.get(voList.size() - 1);
                    if(lastVo.getScore() < score){
                        voList.remove(lastVo);
                        voList.add(vo);
                    }
                }
            }

            //7. 对集合进行重新排序 然后添加到redis
            voList = voList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed()).collect(Collectors.toList());
            cacheService.set(key, JSON.toJSONString(voList));
        }


    }

    private ApArticle updateDB(ArticleVisitStreamMess mess) {
        ApArticle apArticle = getById(mess.getArticleId());

        apArticle.setCollection((apArticle.getCollection()==null?0:apArticle.getCollection())+mess.getCollect());
        apArticle.setComment((apArticle.getComment()==null?0:apArticle.getComment())+mess.getComment());
        apArticle.setLikes((apArticle.getLikes()==null?0:apArticle.getLikes())+mess.getLike());
        apArticle.setViews((apArticle.getViews()==null?0:apArticle.getViews())+mess.getView());

        updateById(apArticle);

        return apArticle;
    }

    //计算文章的得分
    private Integer computeScore(ApArticle apArticle) {
        int score = 0;

        if(null != apArticle.getViews()){
            score += apArticle.getViews();
        }

        if(null != apArticle.getLikes()){
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }

        if(null != apArticle.getComment()){
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }

        if(null != apArticle.getCollection()){
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;
    }
}
